<?php

################################################################
################################################################
########
class cTreePDO
{
#--------------  VAR  --------------#
public $DB=     '';
public $DBName= '';
public $User=   '';
public $Pwd=    '';
public $Host=   '';
public $Table=  '';
public $DriverOptions= array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES UTF8');

public $LogSize= 100;

public $hFields= array(
					'PID'=>           1,
					'M_PATH'=>        1,
					'IS_NODE'=>       1,
					'STATE'=>         1,
					'NAME'=>          1,
					'CONTENT'=>       1,
				);
public $ListLimitStart= 0;
public $ListLimit=      500;

public $OnlyPublished= 0; # только опубликованные
public $OnlyItems=     1; # не показывать узловые

public $sList=  '';
public $hRow=   '';
public $Count=  '';

public $OrderQ= '';
public $Query=  '';

public $hCondition= array(); # условия выборки в виде хэша, пример: array('a'=> '%хрен');
public $hOrder=     array(); # парамеры сортировки, пример:  array('ID'=> 'ASC');

public $NodesMode= 0; # ???

public $Connected= 0;


public $aLog= array();
#--------------  VAR  --------------#

################################################################
########
	public function __construct($Table= '')
	{
		if(isset($GLOBALS['DbName']))   $this->DBName= $GLOBALS['DbName'];
		if(isset($GLOBALS['DbUser']))   $this->User=   $GLOBALS['DbUser'];
		if(isset($GLOBALS['DbPWD']))    $this->Pwd=    $GLOBALS['DbPWD'];
		if(isset($GLOBALS['DbHost']))   $this->Host=   $GLOBALS['DbHost'];
		if($Table)                      $this->Table=  $Table;

		if(@get_class($GLOBALS['DbConnection'])== 'PDO')
		{
			$this->DB= $GLOBALS['DbConnection'];
			$this->Connected= 1;
		}
	}
################################################################
########

	public function Connect()
	{
		$DSN=  'mysql:dbname='.$this->DBName.';';
		$DSN.= 'host='.$this->Host;
		try
		{
			$this->DB= new PDO($DSN, $this->User, $this->Pwd, $this->DriverOptions);
			$this->DB->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
			$GLOBALS['DbConnection']= $this->DB;
			$this->Connected= 1;
			$this->log_prep(__METHOD__.": Ok. =Connected to ".$this->DBName."=");
		}
		catch(PDOException $Err)
		{
			$this->log_prep(__METHOD__." [ERR]: =".$Err->getMessage()."=");
		}
	}
################################################################
########
	function Add($hParams)
	{
		$Fields= '';
		$Datas=  '';
		$ID=     0;

		if(!$hParams['STATE']) $hParams['STATE']= 100;
		if($hParams['IS_NODE']){ $hParams['IS_NODE']= 1;} else{ $hParams['IS_NODE']= 0;}

		if(!$hParams['PID'] || !is_numeric($hParams['PID'])) $hParams['PID']= 0;
		if(is_numeric($hParams['PID']))
		{
			if($hParams['PID']== 0){ $hParams['M_PATH']= '.';}
			else{ $hParams['M_PATH']= $this->GetParentMPath($hParams['PID']).$hParams['PID'].'.';}
		}
		
		if(10< count($this->parse_mpath($V['M_PATH'])))
		{
			$hParams['M_PATH']= '.';
			$hParams['PID']=    0;
		}

		foreach($hParams as $K=> $V)
		{
			if($this->hFields[$K] && $V)
			{
				$Fields.= '`'.$K.'`, ';
				$Datas.=  $this->DB->quote($V).', ';
			}
		}

		$Fields= preg_replace('/,\s*$/', '', $Fields);
		$Datas=  preg_replace('/,\s*$/', '', $Datas);

		$Query= 'INSERT INTO `'.$this->Table.'` ('.$Fields.') VALUES ('.$Datas.')';
		try
		{
			$STH= $this->DB->prepare($Query);
			$STH->execute();
			$ID= $this->DB->lastInsertId();
			$this->log_prep(__METHOD__.": Ok.\n=\n".$Query."\n=");
		}
		catch(PDOException $Err)
		{
			$this->log_prep(__METHOD__." [ERR]: ".$Err->getMessage()."\n=\n".$Query."\n=");
		}

	return($ID);
	}

################################################################
########
	function Update($hParams)
	{
		$Query= '';
		$FLAG=  0;

		if(is_numeric($hParams['ID']))
		{
			foreach($hParams as $K=> $V)
			{
				if($K== 'IS_NODE') continue;
				if($K== 'M_PATH') continue;
				if($this->hFields[$K]) $Query.= '`'.$K.'`='.$this->DB->quote($V).', ';
			}
			
			$Query=  preg_replace('/,\s*$/', '', $Query);
			$Query= 'UPDATE `'.$this->Table.'` SET '.$Query.' WHERE `ID` = '.$this->DB->quote($hParams['ID']);
			try
			{
				$STH= $this->DB->prepare($Query);
				$STH->execute();
				$ID= $this->DB->lastInsertId();

				$FLAG= $hParams['ID'];
				$this->log_prep(__METHOD__.": Ok.\n=\n".$Query."\n=");
			}
			catch(PDOException $Err)
			{
				$this->log_prep(__METHOD__." [ERR]: ".$Err->getMessage()."\n=\n".$Query."\n=");
			}
		}

	return($FLAG);
	}

################################################################
########
	function Del($ID= 0)
	{
		$FLAG=  0;
		$Count= 0;

		if(is_numeric($ID))
		{
			$Query= 'DELETE FROM `'.$this->Table.'` WHERE `ID` = '.$this->DB->quote($ID);
			$Query.= ' OR `M_PATH` LIKE '.$this->DB->quote('%.'.$ID.'.%');
			try
			{
				$Count= $this->DB->exec($Query);
				$this->log_prep(__METHOD__.": Ok.\n=\n".$Query."\n=");
				$FLAG= $ID;
			}
			catch(PDOException $Err)
			{
				$this->log_prep(__METHOD__." [ERR]: ".$Err->getMessage()."\n=\n".$Query."\n=");
			}
		}

	return($FLAG);
	}

################################################################
########
	function Get($ID= 0)
	{
		try
		{
			$Query= 'SELECT * FROM `'.$this->Table.'` WHERE `ID` = '.$this->DB->quote($ID);
			
			$Res= $this->DB->query($Query);
			$this->hRow= $Res->fetch(PDO::FETCH_ASSOC);
			$this->log_prep(__METHOD__.": Ok.\n=\n".$Query."\n=");
		}
		catch(PDOException $Err)
		{
			$this->log_prep(__METHOD__." [ERR]: ".$Err->getMessage()."\n=\n".$Query."\n=");
		}

	return($this->hRow);
	}

################################################################
########
	function GetParentMPath($ID= 0)
	{
		$hRow=  array();
		$MPath= '.';
		try
		{
			$Query= 'SELECT `M_PATH`, `IS_NODE` FROM `'.$this->Table.'` WHERE `ID`= '.$this->DB->quote($ID);

			$Res= $this->DB->query($Query);
			$this->hRow= $Res->fetch(PDO::FETCH_ASSOC);
			$this->log_prep(__METHOD__.": Ok.\n=\n".$Query."\n=");
		}
		catch(PDOException $Err)
		{
			$this->log_prep(__METHOD__." [ERR]: ".$Err->getMessage()."\n=\n".$Query."\n=");
		}
		if($hRow['IS_NODE']) $MPath= $hRow['M_PATH'];

	return($MPath);
	}

################################################################
########
	function List_($ListLimitStart= NULL, $ListLimit= NULL)
	{
		if(!is_int($ListLimitStart)) $ListLimitStart= $this->ListLimitStart;
		if(!is_int($ListLimit))      $ListLimit=      $this->ListLimit;
		
		$Order=     '`ID` ASC';
		$Condition= '';
		$Operator=  '= ';

		if($this->OnlyItems)     $this->hCondition['IS_NODE']= 0;
		if($this->OnlyPublished) $this->hCondition['STATE']= 100;

		if($this->hCondition) $Condition.= $this->prep_conditions($Condition);
		if(!$Condition) $Condition= 1;

		if($this->OrderQ) $Order= $this->OrderQ;
		if($this->hOrder) $Order= $this->prep_order();

		$this->sList= array();
		try
		{
			if(preg_match('/^\s*SELECT/i', $this->Query)){ $Query= $this->Query;}
			else
			{
				$Query= 'SELECT * FROM `'.$this->Table.'` WHERE '.$Condition.' ORDER BY '.$Order;
				$Query.= " LIMIT ".$ListLimitStart.", ".$ListLimit;
			}
			$STH= $this->DB->prepare($Query);
			$STH->execute();
			$this->sList= $STH->fetchAll(PDO::FETCH_ASSOC);

			$this->log_prep(__METHOD__.": Ok.\n=\n".$Query."\n=");
		}
		catch(PDOException $Err)
		{
			$this->log_prep(__METHOD__." [ERR]: ".$Err->getMessage()."\n=\n".$Query."\n=");
		}

		if(preg_match('/^\s*SELECT/i', $this->Query))
		{
			$Query= $this->Query;
			preg_replace('/^.*FROM/i', 'SELECT COUNT(ID) FROM', $Query);
		}
		else{ $Query= 'SELECT COUNT(ID) FROM `'.$this->Table.'` WHERE '.$Condition;}
		try
		{
			$Res= $this->DB->query($Query);
			$sCount= $Res->fetch(PDO::FETCH_ASSOC);
			$this->Count= $sCount['COUNT(ID)'];
			$this->log_prep(__METHOD__.": Ok.\n=\n".$Query."\n=");
		}
		catch(PDOException $Err)
		{
			$this->log_prep(__METHOD__." [ERR]: ".$Err->getMessage()."\n=\n".$Query."\n=");
		}
		$this->Query= '';

	return($this->sList);
	}

################################################################
########
	function Tree()
	{
	$Order= '`ID` ASC ';
	$Condition= '';

		$this->hCondition['IS_NODE']= 1;
		$this->hCondition['M_PATH']= '.';
		if($this->OnlyPublished) $this->hCondition['STATE']= 100;
		if($this->hCondition) $Condition.= $this->prep_conditions($Condition);

		if($this->OrderQ) $Order= $this->OrderQ;
		if($this->hOrder) $Order= $this->prep_order();

		$sList= array();
		try
		{
			$Query= 'SELECT * FROM `'.$this->Table.'` WHERE '.$Condition.' ORDER BY '.$Order;

			$STH= $this->DB->prepare($Query);
			$STH->execute();
			$this->sList= $STH->fetchAll(PDO::FETCH_ASSOC);
			$this->log_prep(__METHOD__.": Ok.\n=\n".$Query."\n=");
		}
		catch(PDOException $Err)
		{
			$this->log_prep(__METHOD__." [ERR]: ".$Err->getMessage()."\n=\n".$Query."\n=");
		}

# количество корневых узлов
		if(preg_match('/^\s*SELECT/i', $this->Query))
		{
			$Query= $this->Query;
			preg_replace('/^.*FROM/i', 'SELECT COUNT(ID) FROM', $Query);
		}
		else{ $Query= 'SELECT COUNT(ID) FROM `'.$this->Table.'` WHERE '.$Condition;}
		try
		{
			$Res= $this->DB->query($Query);
			$sCount= $Res->fetch(PDO::FETCH_ASSOC);
			$this->Count= $sCount['COUNT(ID)'];
			$this->log_prep(__METHOD__.": Ok.\n=\n".$Query."\n=");
		}
		catch(PDOException $Err)
		{
			$this->log_prep(__METHOD__." [ERR]: ".$Err->getMessage()."\n=\n".$Query."\n=");
		}
// количество корневых узлов

		$aChildNodes= array();
		$sTMP3=       array();
		foreach($this->sList as $K=> $V)
		{
			$Condition= "`IS_NODE`= 1 AND `M_PATH` LIKE '.".$V['ID'].".%' ";
			if($this->OnlyPublished) $Condition.= 'AND `STATE`= 100 ';
			$Query= "SELECT * FROM `".$this->Table."` WHERE ".$Condition." ORDER BY `ID` ASC";
			try
			{
				$STH= $this->DB->prepare($Query);
				$STH->execute();
				$aTMP= $STH->fetchAll(PDO::FETCH_ASSOC);

				$aChildNodes= array_merge($aChildNodes, $aTMP);

				$this->log_prep(__METHOD__." Ok.\n=\n".$Query."\n=");
			}
			catch(PDOException $Err)
			{
				$this->log_prep(__METHOD__." [ERR]: ".$Err->getMessage()."\n=\n".$Query."\n=");
			}
			$sTMP3[$V['ID']]= $V;
			$sTMP3[$V['ID']]['LEVEL']= 0;
		}
		$this->sList= $sTMP3;
		unset($sTMP3);

		$sTMP1= array();
		$MaxL= 0;
		foreach($aChildNodes as $K=> $V)
		{
			$V['LEVEL']= count($this->parse_mpath($V['M_PATH']));
			$sTMP1[$V['ID']]= $V;
			if($MaxL< $V['LEVEL']) $MaxL= $V['LEVEL'];
		}
		$sTMP2= $sTMP1;

		foreach($sTMP1 as $K=> $V)
		{
			$aMPath= $this->parse_mpath($V['M_PATH']);
			foreach($sTMP2 as $KK=> $VV)
			{
				if($sTMP2[$aMPath[$i]]['ID'] && $sTMP2[$aMPath[$i+ 1]]['ID'])
					$sTMP2[$aMPath[$i]]['CH'][$aMPath[$i+ 1]]= $sTMP2[$aMPath[$i+ 1]];
			}
		}
		
		for($i= 0; $i<= $MaxL; $i++)
		{
			foreach($sTMP2 as $K=> $V)
			{
				if($V['PID']) $sTMP2[$V['PID']]['CH'][$V['ID']]= $V;
				if(!$V['ID']){ unset($sTMP2[$K]);} 
			}
		}

		foreach($sTMP2 as $K=> $V)
		{
			if($this->sList[$V['PID']]) $this->sList[$V['PID']]['CH'][$V['ID']]= $V;
		}

	return($this->sList);
	}

################################################################
########
	function LogGet()
	{
	return($this->aLog);
	}

################################################################
########
	function LogAdd($Str= '')
	{
		$this->log_prep($Str);
	return(1);
	}

################################################################
########
	function FieldsGet()
	{
	return($this->hFields);
	}

################################################################
########
	private function log_prep($Str)
	{
		if($this->LogSize< count($this->aLog)) array_shift($this->aLog);
		$this->aLog[]= date("H:i:s, d.m.y")."> ".$Str;

	return($Str);
	}
	
################################################################
########
	private function prep_conditions($Condition= '')
	{
		$Content= '';
		$Operator=  '= ';

		if($Condition) $Content.= 'AND ';
		foreach($this->hCondition as $K=> $V)
		{
			if($this->hFields[$K] || $K== 'ID')
			{
				$Content.= '`'.$K.'` ';
				if(preg_match('/%/', $V)) $Operator= 'LIKE ';
				$Content.= $Operator;
				$Operator= '= ';

				$Content.= $this->DB->quote($V).' AND ';
			}
		}
		$Content= preg_replace('/\s*AND\s*$/i', '', $Content);

	return($Content);
	}

################################################################
########
	private function prep_order()
	{
		$Content= '';
		foreach($this->hOrder as $K=> $V)
		{
			if($this->hFields[$K] || $K== 'ID')
			{
				$Content.= '`'.$K.'` ';
				if($V== 'ASC'){ $Content.= 'ASC, ';} else{ $Content.= 'DESC, ';}
			}
		}
		$Content= preg_replace('/\s*,\s*$/i', '', $Content);

	return($Content);
	}

################################################################
########
	private function parse_mpath($Str= '')
	{
	$aList= array();

		$aList= split("\.", trim($Str, "."));

	return($aList);
	}
}

?>